﻿using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Infrastructure;
using Infrastructure.DataTypes;
using System.ComponentModel;
using Algorithms;
using StatisticalTest;
namespace Controllers
{
    public class Dispatcher
    {
        DataSet<Discrete> m_data;//data source
        PredictionAlgorithmController<Discrete> m_PredictionControl = new PredictionAlgorithmController<Discrete>();//prediction algorithm controller initiation
        StatisticalAnalysisController<Discrete> m_StatisticalAlgorithm = new StatisticalAnalysisController<Discrete>();
        public BackgroundWorker BackgroundWorker { private get; set; }
        
        /// <summary>
        /// Load data to controller
        /// </summary>
        /// <param name="data_source">data source name</param>
        public void LoadData(string dataSource, string user, string userID)
        {
            switch (dataSource)
            {
                case "MovieLens 100K": DataSetLoaderMovieLens loader = new DataSetLoaderMovieLens(BackgroundWorker);
                    m_data = loader.Loader();
                    break;
                case "MovieLens 1M":
                    DataSetLoaderMovieLens1Mil loader1M = new DataSetLoaderMovieLens1Mil(BackgroundWorker);
                    m_data = loader1M.Loader();
                    break;
                default:
                    break;
            }
           
        }
        /// <summary>
        /// return list of data sources
        /// </summary>
        /// <returns></returns>
        /// 
        public void LoadDataOfLogInUser(string dataSource, string user, string userID)
        {
            if (File.Exists(userID + dataSource + "Scores.txt"))
            {
                try
                {
                    StreamReader strm = new StreamReader(userID + dataSource + "Scores.txt");

                    string line = strm.ReadLine();
                    while (line != null)
                    {
                        string[] dt = line.Split(' ');
                        Discrete score = System.Convert.ToInt16(dt[1].Trim());
                        AddNewScoreToDataSet(userID, dt[0], score);
                        line = strm.ReadLine();
                    }
                }
                catch (Exception)
                {

                    throw;
                }

            }
        }
        public List<string> dataSource()
        {
            List<string> data_source = new List<string>();
            data_source.Add("MovieLens 100K");
            data_source.Add("MovieLens 1M");
            return data_source;
        }

        /// <summary>
        /// gets list of activated algorithmes 
        /// </summary>
        /// <returns></returns>
        public ICollection<string> PredictionAlgorithmList()
        {
           return m_PredictionControl.PredictionAlgorithmsList;
        }
        /// <summary>
        /// gets list of impemented statistical test 
        /// </summary>
        /// <returns></returns>
        public List<string> StatisticalAnalysisAlgorithmList()
        {
            return m_StatisticalAlgorithm.ImplementedTests;
        }
        /// <summary>
        /// gets list top N items 
        /// </summary>
        /// <returns></returns>
        public List<Item<Discrete>> TopNItems(int n)
        {
            return m_data.getSortedItemsByRating(n);
        }
        /// <summary>
        /// gets list of all users
        /// </summary>
        /// <returns></returns>
        public IEnumerable<User<Discrete>> UsersList()
        {
            return m_data.Users.Values;
        }

        public List<string> GetUserIds()
        {
            return m_data.Users.Keys.ToList();
        }

        public User<Discrete> GetUser(string userID)
        {
            return m_data.Users[userID];
        }
        /// <summary>
        /// resive active user that chosen randomly
        /// </summary>
        /// <returns></returns>
        public User<Discrete> PickRandomUser()
        {
            return m_data.GenerateRandomUser();
        }
        /// <summary>
        /// generate user Id and add new user to dataset
        /// </summary>
        /// <returns></returns>
        public User<Discrete> GenerateUserID() 
        {
            return m_data.generateUseID();
        }
        /// <summary>
        /// adding new scores to dataset
        /// </summary>
        /// <param name="user"></param>
        /// <param name="item"></param>
        /// <param name="score"></param>
        public void AddNewScoreToDataSet(string userID,string itemID,Discrete score)
        {
            User<Discrete>user=new User<Discrete>(userID);
            Item<Discrete>item=new Item<Discrete>(itemID);
            m_data.addUserItemRating(user, item, score);

        }

/// <summary>
/// add vote to all Items from genre , given by the same user
/// </summary>
/// <param name="userID"></param>
/// <param name="gener"></param>

        public void AddNewScoreToDataSetByRelatedGenre(string userID, string gener)
        {
            foreach (Item<Discrete> item in m_data.Items.Values)
            {
                if (item.Data_Attributes.ContainsKey(gener.Trim()))
                {
                    double temp=Math.Round(item.StatisticsAttributes.AverageRating,MidpointRounding.AwayFromZero);
                    Discrete score = System.Convert.ToInt16(temp);
                    AddNewScoreToDataSet(userID, item.ItemID,score);
                }
            }
           
        }
        /// <summary>
        /// get all items and the scores that the user rated for them
        /// </summary>
        /// <param name="userID"></param>
        /// <returns></returns>
        public Dictionary<Item<Discrete>, Discrete> GetUserVoteHistory(string userID)
        {
            if (m_data.Users.ContainsKey(userID))
            {
                return m_data.Users[userID].Votes;
            }
            return new Dictionary<Item<Discrete>, Discrete>();

        }

        /// <summary>
        /// get all items that had not recieved scores from user 
        /// </summary>
        /// <param name="userID"></param>
        /// <returns></returns>
        public List<Item<Discrete>> ItemsNotVotedByUser(string userID)
        {
            if (m_data.Users.ContainsKey(userID))
            {
                return m_data.getItemsNotRatedByUser(m_data.Users[userID]).ToList();
            }
            return new List<Item<Discrete>>();
        }
        /// <summary>
        /// get all items in data set
        /// </summary>
        /// <returns></returns>
        public List<KeyValuePair<string, string>> GetAllItems()
        {
            List<KeyValuePair<string, string>> ans = new List<KeyValuePair<string, string>>();
            foreach (Item<Discrete> item in m_data.Items.Values)
            {
                if (item.Data_Attributes.ContainsKey("title"))
                {
                    ans.Add(new KeyValuePair<string, string>(item.ItemID, item.Data_Attributes["title"]));
                }
                
            }
            return ans;
        }

        /// <summary>
        /// get all items that were scored by user without scores
        /// </summary>
        /// <param name="userID"></param>
        /// <returns></returns>
        public List<Item<Discrete>> ItemsVotedByUser(string userID)
        {
            if (m_data.Users.ContainsKey(userID))
            {
                return m_data.Users[userID].Votes.Keys.ToList();
            }
            return new List<Item<Discrete>>();
        }

        /// <summary>
        /// get item with item ID
        /// </summary>
        /// <param name="itemID"></param>
        /// <returns></returns>
        public Item<Discrete> GetItem(string itemID)
        {
            if (m_data.Items.ContainsKey(itemID))
            {
                return m_data.Items[itemID];
            }
            return null;
        }

        /// <summary>
        /// activate inner prediction algorithm 
        /// </summary>
        /// <param name="userID">user ID</param>
        /// <param name="itemID">item ID</param>
        /// <param name="predictionType">prediction algorithm name</param>
        /// <returns></returns>
        public double ActivatePredictionAlg(string userID, string itemID, string predictionType)
        {
            if (m_data.Items.ContainsKey(itemID) && m_data.Users.ContainsKey(userID))
            {
                return m_PredictionControl.Prediction(m_data.Users[userID], m_data.Items[itemID], predictionType);
                
            }
            return double.NaN;
        }


        public void WriteScoresToFile(string userID,string dataSource,Dictionary<string,Discrete> items)
        {
            string source=userID + dataSource + "Scores.txt";
            StreamWriter wrt;
            if(File.Exists(userID + dataSource + "Scores.txt"))
            {
                wrt = new StreamWriter(source, true);
            }
            else
            {
                wrt = File.CreateText(source);
            }
            foreach (KeyValuePair<string,Discrete> item in items)
            {
                wrt.WriteLine(item.Key + " " + item.Value.ToString());
            }
            wrt.Close();
        }

        public Dictionary<string, double> RunStatisticalAnalysis(int n, string test, List<string> lAlgorithmNames)
        {
            m_StatisticalAlgorithm.BackgroundWorker = BackgroundWorker;
            Dictionary<string, double> ans = m_StatisticalAlgorithm.RunStatisticalAnalysis(m_data.getListOfRandomUsers(n), test, lAlgorithmNames);
            return ans;
        }
       
    }
}
